﻿using System;
using System.IO;
using Beaker.Specs;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenCube.IO.Specs.Builders;

namespace OpenCube.IO.Specs
{
	[TestClass]
	[CLSCompliant(false)]
	public class EndiannessAwareBinaryReaderSpecs
		: SpecsFor<EndiannessAwareBinaryReader, BigEndianBinaryReaderBuilder>
	{
		[TestMethod]
		public void ByteShouldRead()
		{
			Stream stream = new ByteStreamBuilder()
				.AppendBytes(0xfe)
				.Build();

			BuildSubject
				.FromStream(stream);

			byte result = Subject.ReadByte();

			result.Should().Be(254);
		}

		[TestMethod]
		public void ByteShoudThrowWhenNotEnoughBytes()
		{
			Stream stream = new ByteStreamBuilder()
				.Build();

			BuildSubject
				.FromStream(stream);

			Action act = () => Subject.ReadByte();

			act.ShouldThrow<EndOfStreamException>();
		}

		[TestMethod]
		public void SByteShouldRead()
		{
			Stream stream = new ByteStreamBuilder()
			.AppendBytes(0xfe)
			.Build();

			BuildSubject
				.FromStream(stream);

			sbyte result = Subject.ReadSByte();

			result.Should().Be(-2);
		}

		[TestMethod]
		public void SByteShoudThrowWhenNotEnoughBytes()
		{
			Stream stream = new ByteStreamBuilder()
				.Build();

			BuildSubject
				.FromStream(stream);

			Action act = () => Subject.ReadSByte();

			act.ShouldThrow<EndOfStreamException>();
		}

		[TestMethod]
		public void Int16ShouldBeReadBigEndian()
		{
			Stream stream = new ByteStreamBuilder()
				.AppendBytes(0xfe, 0xdc)
				.Build();

			BuildSubject
				.FromStream(stream);

			short result = Subject.ReadInt16();

			result.Should().Be(-292);
		}

		[TestMethod]
		public void Int16ShoudThrowWhenNotEnoughBytes()
		{
			Stream stream = new ByteStreamBuilder()
				.AppendRandomBytes(1)
				.Build();

			BuildSubject
				.FromStream(stream);

			Action act = () => Subject.ReadInt16();

			act.ShouldThrow<EndOfStreamException>();
		}

		[TestMethod]
		public void UInt16ShouldBeReadBigEndian()
		{
			Stream stream = new ByteStreamBuilder()
				.AppendBytes(0xfe, 0xdc)
				.Build();

			BuildSubject
				.FromStream(stream);

			ushort result = Subject.ReadUInt16();

			result.Should().Be(65244);
		}

		[TestMethod]
		public void UInt16ShoudThrowWhenNotEnoughBytes()
		{
			Stream stream = new ByteStreamBuilder()
				.AppendRandomBytes(1)
				.Build();

			BuildSubject
				.FromStream(stream);

			Action act = () => Subject.ReadUInt16();

			act.ShouldThrow<EndOfStreamException>();
		}

		[TestMethod]
		public void Int24houldBeReadBigEndian()
		{
			Stream stream = new ByteStreamBuilder()
				.AppendBytes(0xfe, 0xdc, 0xba)
				.Build();

			BuildSubject
				.FromStream(stream);

			int result = Subject.ReadInt24();

			result.Should().Be(-74566);
		}

		[TestMethod]
		public void Int24ShoudThrowWhenNotEnoughBytes()
		{
			Stream stream = new ByteStreamBuilder()
				.AppendRandomBytes(2)
				.Build();

			BuildSubject
				.FromStream(stream);

			Action act = () => Subject.ReadInt24();

			act.ShouldThrow<EndOfStreamException>();
		}

		[TestMethod]
		public void UInt24houldBeReadBigEndian()
		{
			Stream stream = new ByteStreamBuilder()
				.AppendBytes(0xfe, 0xdc, 0xba)
				.Build();

			BuildSubject
				.FromStream(stream);

			uint result = Subject.ReadUInt24();

			result.Should().Be(16702650);
		}

		[TestMethod]
		public void UInt24ShoudThrowWhenNotEnoughBytes()
		{
			Stream stream = new ByteStreamBuilder()
				.AppendRandomBytes(2)
				.Build();

			BuildSubject
				.FromStream(stream);

			Action act = () => Subject.ReadUInt24();

			act.ShouldThrow<EndOfStreamException>();
		}

		[TestMethod]
		public void Int32ShouldBeReadBigEndian()
		{
			Stream stream = new ByteStreamBuilder()
				.AppendBytes(0xfe, 0xdc, 0xba, 0x98)
				.Build();

			BuildSubject
				.FromStream(stream);

			int result = Subject.ReadInt32();

			result.Should().Be(-19088744);
		}

		[TestMethod]
		public void Int32ShouldThrowWhenNotEnoughBytes()
		{
			Stream stream = new ByteStreamBuilder()
				.AppendRandomBytes(3)
				.Build();

			BuildSubject
				.FromStream(stream);

			Action act = () => Subject.ReadInt32();

			act.ShouldThrow<EndOfStreamException>();
		}

		[TestMethod]
		public void UInt32ShouldBeReadBigEndian()
		{
			Stream stream = new ByteStreamBuilder()
				.AppendBytes(0xfe, 0xdc, 0xba, 0x98)
				.Build();

			BuildSubject
				.FromStream(stream);

			uint result = Subject.ReadUInt32();

			result.Should().Be(4275878552);
		}

		[TestMethod]
		public void UInt32ShouldThrowWhenNotEnoughBytes()
		{
			Stream stream = new ByteStreamBuilder()
				.AppendRandomBytes(3)
				.Build();

			BuildSubject
				.FromStream(stream);

			Action act = () => Subject.ReadUInt32();

			act.ShouldThrow<EndOfStreamException>();
		}

		[TestMethod]
		public void Int64ShouldBeReadBigEndian()
		{
			Stream stream = new ByteStreamBuilder()
				.AppendBytes(0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10)
				.Build();

			BuildSubject
				.FromStream(stream);

			long result = Subject.ReadInt64();

			result.Should().Be(-81985529216486896);
		}

		[TestMethod]
		public void Int64ShouldThrowWhenNotEnoughBytes()
		{
			Stream stream = new ByteStreamBuilder()
				.AppendRandomBytes(7)
				.Build();

			BuildSubject
				.FromStream(stream);

			Action act = () => Subject.ReadInt64();

			act.ShouldThrow<EndOfStreamException>();
		}

		[TestMethod]
		public void UInt64ShouldBeReadBigEndian()
		{
			Stream stream = new ByteStreamBuilder()
				.AppendBytes(0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10)
				.Build();

			BuildSubject
				.FromStream(stream);

			ulong result = Subject.ReadUInt64();

			result.Should().Be(0xfedcba9876543210);
		}

		[TestMethod]
		public void UInt64ShouldThrowWhenNotEnoughBytes()
		{
			Stream stream = new ByteStreamBuilder()
				.AppendRandomBytes(7)
				.Build();

			BuildSubject
				.FromStream(stream);

			Action act = () => Subject.ReadUInt64();

			act.ShouldThrow<EndOfStreamException>();
		}

		[TestMethod]
		public void SingleShouldBeReadBigEndian()
		{
			Stream stream = new ByteStreamBuilder()
				.AppendBytes(0xfe, 0xdc, 0xba, 0x98)
				.Build();

			BuildSubject
				.FromStream(stream);

			float result = Subject.ReadSingle();

			result.Should().Be(-1.466995e+38f);
		}

		[TestMethod]
		public void SingleShouldThrowWhenNotEnoughBytes()
		{
			Stream stream = new ByteStreamBuilder()
				.AppendRandomBytes(3)
				.Build();

			BuildSubject
				.FromStream(stream);

			Action act = () => Subject.ReadSingle();

			act.ShouldThrow<EndOfStreamException>();
		}

		[TestMethod]
		public void DoubleShouldBeReadBigEndian()
		{
			Stream stream = new ByteStreamBuilder()
				.AppendBytes(181, 162, 85, 68, 129, 8, 176, 198)
				.Build();

			BuildSubject
				.FromStream(stream);

			double result = Subject.ReadDouble();

			result.Should().Be(-2.45e-50d);

		}

		[TestMethod]
		public void DoubleShouldThrowWhenNotEnoughBytes()
		{
			Stream stream = new ByteStreamBuilder()
				.AppendRandomBytes(7)
				.Build();

			BuildSubject
				.FromStream(stream);

			Action act = () => Subject.ReadDouble();

			act.ShouldThrow<EndOfStreamException>();
		}

		[TestMethod]
		public void ReadBytesShouldBeReadAsIs()
		{
			Stream stream = new ByteStreamBuilder()
				.AppendBytes(0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10)
				.Build();

			BuildSubject
				.FromStream(stream);

			byte[] result = Subject.ReadBytes(8);

			// result.Should().Be().Equal(...) on byte array returns false when actual true... cast problem insdie code?
			result[0].Should().Be(0xfe);
			result[1].Should().Be(0xdc);
			result[2].Should().Be(0xba);
			result[3].Should().Be(0x98);
			result[4].Should().Be(0x76);
			result[5].Should().Be(0x54);
			result[6].Should().Be(0x32);
			result[7].Should().Be(0x10);
		}

		[TestMethod]
		public void ReadBytesShouldThrowWhenNotEnoughBytes()
		{
			Stream stream = new ByteStreamBuilder()
				.AppendRandomBytes(7)
				.Build();

			BuildSubject
				.FromStream(stream);

			Action act = () => Subject.ReadBytes(8);

			act.ShouldThrow<EndOfStreamException>();
		}

		[TestMethod]
		public void ReadSBytesShouldBeReadAsIs()
		{
			Stream stream = new ByteStreamBuilder()
				.AppendBytes(0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10)
				.Build();

			BuildSubject
				.FromStream(stream);

			sbyte[] result = Subject.ReadSBytes(8);

			// result.Should().Be().Equal(...) on byte array returns false when actual true... cast problem insdie code?
			result[0].Should().Be(-2);
			result[1].Should().Be(-36);
			result[2].Should().Be(-70);
			result[3].Should().Be(-104);
			result[4].Should().Be(118);
			result[5].Should().Be(84);
			result[6].Should().Be(50);
			result[7].Should().Be(16);
		}

		[TestMethod]
		public void ReadSBytesShouldThrowWhenNotEnoughBytes()
		{
			Stream stream = new ByteStreamBuilder()
				.AppendRandomBytes(7)
				.Build();

			BuildSubject
				.FromStream(stream);

			Action act = () => Subject.ReadSBytes(8);

			act.ShouldThrow<EndOfStreamException>();
		}
	}
}
